﻿using Microsoft.Extensions.Logging;
using System.Diagnostics;
using System.Text;

namespace MiniJob.Worker.Jobs;

/// <summary>
/// 脚本处理器基类
/// </summary>
public abstract class ScriptJobBase : Job
{
    protected virtual List<string> DownloadProtocol { get; set; } = new List<string> { "http", "https", "ftp" };

    protected override async Task<JobResult> DoWorkAsync(JobContext context, CancellationToken cancellationToken = default)
    {
        Logger.LogInformation("start to process ScriptProcessor");

        var para = context.GetParams();
        if (para.IsNullOrWhiteSpace())
        {
            return JobResult.ErrorMessage("params is null.");
        }

        var script = await PrepareScriptFile(context.InstanceId, para);

        Process process = new()
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = GetRunCommand(),
                UseShellExecute = false,
                CreateNoWindow = true,
                RedirectStandardError = true,
                RedirectStandardInput = true,
                RedirectStandardOutput = true
            }
        };

        StringBuilder outputBuilder = new StringBuilder();
        StringBuilder errorBuilder = new StringBuilder();
        string result;

        try
        {
            process.Start();
            await process.StandardInput.WriteLineAsync(script);
            process.StandardInput.AutoFlush = true;

            await CopyStream(process.StandardOutput, outputBuilder);
            await CopyStream(process.StandardError, errorBuilder);

            await process.WaitForExitAsync(cancellationToken);
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "ScriptProcessor has been interrupted");
        }
        finally
        {
            var error = errorBuilder.ToString();
            if (!error.IsNullOrWhiteSpace())
                outputBuilder.AppendLine($"[Error]: {error}");
            result = $"[Output]: {outputBuilder}";
        }

        return new JobResult(true, result);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="instanceId"></param>
    /// <param name="processorInfo"></param>
    /// <returns></returns>
    /// <exception cref="IOException"></exception>
    private Task<string> PrepareScriptFile(long instanceId, string processorInfo)
    {
        string script = processorInfo;

        // 如果是下载链接，则从网络获取
        foreach (var protocol in DownloadProtocol)
        {
            if (processorInfo.StartsWith(protocol))
            {
                // todo: 读取网络文本
                script = "";
                break;
            }
        }

        // 命令末尾需要添加退出指令，否则当调用ReadToEnd()方法时，会处于假死状态 
        script = script.Replace("\"", "\\\"");
        if (!script.Contains("exit", StringComparison.OrdinalIgnoreCase))
        {
            script = script + Environment.NewLine + "exit";
        }

        return Task.FromResult(script);
    }

    private async Task CopyStream(StreamReader streamReader, StringBuilder stringBuilder)
    {
        try
        {
            while (!streamReader.EndOfStream)
            {
                var line = await streamReader.ReadLineAsync();
                stringBuilder.AppendLine(line);
                // todo: 同步到在线日志
                Logger.LogInformation(line);
            }
        }
        catch (Exception ex)
        {
            Logger.LogWarning(ex, "CopyStream failed.");
            stringBuilder.AppendLine($"Exception: {ex.Message}");
        }
    }

    /// <summary>
    /// 获取运行命令（eg，shell返回 /bin/sh）
    /// </summary>
    /// <returns>执行脚本的命令</returns>
    protected abstract string GetRunCommand();
}
